package com.encrypt.classloader;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

/**
 * 抽象密钥型解密加载器
 * 
 * @author yiyongpeng
 * 
 */
public abstract class AbstractDecryptClassLoader implements IDecryptClassLoader {
	private Cipher cipher;

	public AbstractDecryptClassLoader(Cipher cipher) {
		this.cipher = cipher;
	}

	public byte[] loadClassData(String name) throws ClassNotFoundException {
		try {
			name = name.replace('.', '/').concat(".class");
			byte[] data = loadFileData(name);
			if (data != null)
				return cipher.doFinal(data);
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		}
		return null;
	}

	protected abstract byte[] loadFileData(String fileName)
			throws ClassNotFoundException;

	/**
	 * 通过私钥解密公钥，并创建解密算法
	 * 
	 * @param publicKeyUrl
	 *            System.getProperty("cl.cert") => System.getenv("CL_CERT")
	 * @param privateKeyUrl
	 *            System.getProperty("cl.key") => System.getenv("CL_KEY")
	 * @return
	 */
	public static Cipher getCipher(URL publicKeyUrl, URL privateKeyUrl) {
		// 没有指定就使用系统属性
		String url = null;
		if (publicKeyUrl == null)
			try {
				url = System.getProperty("cl.cert");
				if (url != null)
					publicKeyUrl = new URL(url);
			} catch (MalformedURLException e) {
				try {
					publicKeyUrl = new URL("file:" + url);
				} catch (MalformedURLException e1) {
					e.printStackTrace();
				}
			}
		if (privateKeyUrl == null)
			try {
				url = System.getProperty("cl.key");
				if (url != null)
					privateKeyUrl = new URL(url);
			} catch (MalformedURLException e) {
				try {
					privateKeyUrl = new URL("file:" + url);
				} catch (MalformedURLException e1) {
					e.printStackTrace();
				}
			}
		// 系统属性中没有就使用环境变量
		if (publicKeyUrl == null)
			try {
				url = System.getenv("CL_CERT");
				if (url != null)
					publicKeyUrl = new URL(url);
			} catch (MalformedURLException e) {
				try {
					publicKeyUrl = new URL("file:" + url);
				} catch (MalformedURLException e1) {
					e.printStackTrace();
				}
			}
		if (privateKeyUrl == null)
			try {
				url = System.getenv("CL_KEY");
				if (url != null)
					privateKeyUrl = new URL(url);
			} catch (MalformedURLException e) {
				try {
					privateKeyUrl = new URL("file:" + url);
				} catch (MalformedURLException e1) {
					e.printStackTrace();
				}
			}

		// 拆包并生成解密算法
		ObjectInputStream privateInputStream = null;
		InputStream publicInputStream = null;
		try {
			if (privateKeyUrl == null) {
				throw new NullPointerException("私钥不存在！");
			}
			if (publicKeyUrl == null) {
				throw new NullPointerException("公钥不存在！");
			}
			// 反序列化私钥对象
			privateInputStream = new ObjectInputStream(new BufferedInputStream(
					privateKeyUrl.openStream()));
			SecretKey privateKey = (SecretKey) privateInputStream.readObject();
			// 读取并解密公钥文件
			publicInputStream = new BufferedInputStream(
					publicKeyUrl.openStream());
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			byte[] buf = new byte[1024];
			for (int size; (size = publicInputStream.read(buf)) > 0;)
				out.write(buf, 0, size);
			buf = out.toByteArray();
			Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
			cipher.init(Cipher.UNWRAP_MODE, privateKey);
			Key key = cipher.unwrap(buf, privateKey.getAlgorithm(),
					Cipher.SECRET_KEY);
			cipher.init(Cipher.DECRYPT_MODE, key);
			return cipher;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} finally {
			if (privateInputStream != null) {
				try {
					privateInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (publicInputStream != null) {
				try {
					publicInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public static URL getURL(File file) {
		if (file.exists()) {
			String name = file.getAbsolutePath().replace('\\', '/');
			if (!name.startsWith("/"))
				name = "/" + name;
			name = "file:" + name;
			try {
				return new URL(name);
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}
